/**
 * 
 */
package com.patternity.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Represents a template of a model about a particular design decision (pattern,
 * idiom...). This includes meta-data such as whether it is a stereotype, and
 * which other design decisions are implied by this one.
 * 
 * This is especially useful to decide what elements to show or hide for
 * display.
 * 
 * @author Cyrille.Martraire
 */
// @ValueObject
public class Motif {

	/**
	 * Represents the mode of declaration for a participant
	 * 
	 * @author Cyrille.Martraire
	 */
	public enum DeclarationMode {

		SITE_OF_DECLARATION,

		DECLARED_BY_ATTRIBUTE_NAMED,

		INFERRED_AS_SUBTYPE_OF_PARTICIPANT;
	}

	/**
	 * Represents a participant within a pattern
	 * 
	 * @author Cyrille.Martraire
	 */
	public static final class Participant {
		private final String name;

		private final DeclarationMode mode;

		private final String declarationKey;

		public Participant(final String name, final DeclarationMode mode,
				final String declarationKey) {
			this.name = name;
			this.mode = mode;
			this.declarationKey = declarationKey;
		}

		public String getName() {
			return name;
		}

		public DeclarationMode getMode() {
			return mode;
		}

		public String getDeclarationKey() {
			return declarationKey;
		}

		@Override
		public String toString() {
			return "Participant "
					+ name
					+ " "
					+ mode
					+ (declarationKey == null ? "" : " (" + declarationKey
							+ ")");
		}
	}

	private final List<Participant> participants = new ArrayList<Participant>();

	private final List<Relation> relations = new ArrayList<Relation>();

	public List<Participant> getParticipants() {
		return participants;
	}

	public Participant getDeclaredParticipant() {
		for (final Participant participant : participants) {
			if (participant.getMode() == DeclarationMode.SITE_OF_DECLARATION) {
				return participant;
			}
		}
		return null;
	}

	public boolean isStereotype() {
		return participants.isEmpty();
	}

	public List<Relation> getRelations() {
		return relations;
	}

	// Fluent interface
	public Participant addParticipant(final Participant p) {
		participants.add(p);
		return p;
	}

	// Fluent interface
	public Relation addRelation(final Relation r) {
		relations.add(r);
		return r;
	}

	public Motif merge(final Motif templateBis) {
		final Motif merged = new Motif();
		final Map<String, Participant> map = new HashMap<String, Motif.Participant>();
		for (final Participant participant : participants) {
			map.put(participant.getName(), participant);
		}
		for (final Participant participant : templateBis.participants) {
			map.put(participant.getName(), participant);
		}
		merged.participants.addAll(map.values());
		merged.relations.addAll(relations);
		merged.relations.addAll(templateBis.relations);
		return merged;
	}

	@Override
	public String toString() {
		final StringBuilder sb = new StringBuilder("Motif ");
		sb.append("\n* Participants");
		for (final Participant participant : participants) {
			sb.append("\n");
			sb.append(participant);
		}
		sb.append("\n* Relations");
		for (final Relation relation : relations) {
			sb.append("\n");
			sb.append(relation);
		}
		return sb.toString();
	}

}
